﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="Global.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;

    using ExulLibrary.Mathematics.LinearAlgebra.Exceptions;

    #endregion

    /// <summary>
    ///     Provides global static methods to organize registration of object types and factories.
    /// </summary>
    [SuppressMessage(
        "Microsoft.Naming",
        "CA1716:IdentifiersShouldNotMatchKeywords",
        MessageId = "Global",
        Justification = "This class is not intended to be used by user code.")]
    public static class Global
    {
        #region Fields

        #region private

        /// <summary>
        ///     The name of base type for all matrices.
        /// </summary>
        private static readonly Type MatrixTypeName = typeof(Matrix<>);

        /// <summary>
        ///     The collection of registered types of matrices.
        /// </summary>
        private static readonly List<string> MatrixTypes = new List<string>();

        /// <summary>
        ///     Private object to organize synchronization.
        /// </summary>
        private static readonly object Sync = new object();

        #endregion

        #endregion

        #region Methods

        #region public

        /// <summary>
        ///     Gets the registration index of the matrix.
        /// </summary>
        /// <param name="matrixType">
        ///     The type of the matrix.
        /// </param>
        /// <returns>
        ///     The registration index of the matrix.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="matrixType"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="GenericTypeDefinitionRegistrationException">
        ///     <paramref name="matrixType"/> represents is generic type definition.
        /// </exception>
        /// <exception cref="NonMatrixTypeRegistrationException">
        ///     <paramref name="matrixType"/> is not inherited from <see cref="Matrix{T}"/>.
        /// </exception>
        public static int GetMatrixRegistrationIndex(Type matrixType)
        {
            if (matrixType == null)
            {
                throw new ArgumentNullException("matrixType");
            }

            if (matrixType.IsGenericTypeDefinition)
            {
                throw new GenericTypeDefinitionRegistrationException("matrixType", matrixType);
            }

            // try to find ExulLibrary.Mathematics.LinearAlgebra as base type
            var type = matrixType.BaseType;
            while (
                (type != null) &&
                !(type.IsGenericType && (type.GetGenericTypeDefinition().FullName == MatrixTypeName.FullName)))
            {
                type = matrixType.BaseType;
            }

            if (type == null)
            {
                throw new NonMatrixTypeRegistrationException("matrixType", matrixType, MatrixTypeName);
            }

            lock (Sync)
            {
                // for generic types registration is performed for generic type definition
                if (matrixType.IsGenericType)
                {
                    matrixType = matrixType.GetGenericTypeDefinition();
                }

                var genericTypeName = matrixType.GetGenericTypeDefinition().FullName;
                var index = MatrixTypes.IndexOf(genericTypeName);
                if (0 <= index)
                {
                    return index;
                }

                MatrixTypes.Add(genericTypeName);
                return MatrixTypes.Count - 1;
            }
        }

        #endregion

        #endregion
    }
}